package jp.co.worksap.ate.gomoku.localai;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import jp.co.worksap.ate.gomoku.model.GoPoint;
import jp.co.worksap.ate.gomoku.model.GoState;
import jp.co.worksap.ate.gomoku.model.Goban;
import jp.co.worksap.ate.gomoku.model.IGomokuAI;

public class MultiLevelAI implements IGomokuAI {

	@Override
	public GoPoint nextMove(Goban goban, GoState goType, int aiLevel) {
		/*if(aiLevel == 1) {
			SingleLevelAI singleLevelAI = new SingleLevelAI();
			return singleLevelAI.nextMove(goban, goType, aiLevel);
		}*/
		List<GoNode> nodeList = LocalAIUtil.getAllValueableEmptyGoNode(goban, 2);
		List<GobanNode> rootList = new ArrayList<GobanNode>();
		for(GoNode goNode : nodeList) {
			GobanNode rootNode = new GobanNode(goban.clone(), goType, goNode.getX(), goNode.getY());
			rootList.add(rootNode);
		}
		cutChild(rootList);
		double max = -Double.MAX_VALUE;
		int level = aiLevel * 2 -1;
		nodeList.clear();
		int nodeNum = 0;
		for(GobanNode rootNode : rootList) {
			GoNode goNode = new GoNode(rootNode.getX(), rootNode.getY());
			GobanTree tree = new GobanTree(goban.clone(), goType, goNode.getX(), goNode.getY());
			//System.out.println("Point:"+rootNode.getX()+","+rootNode.getY());
			computeGobanTree(tree, tree.getRoot(), level, level);
			nodeNum += tree.getNodeNum();
			double score = tree.getSocre() - tree.getEnemyScore() + tree.getRoot().getDefenceScore();
			goNode.setScore(score);
			nodeList.add(goNode);
			if(score > max) {
				max = score;
			}
		}
		List<GoNode> maxList = new ArrayList<GoNode>();
		for(GoNode node : nodeList) {
			if(Math.abs(max - node.getScore())<(Math.abs(max)/10)) {
				maxList.add(node);
			}
		}
		GoNode node;
		if(maxList.size()==1) {
			node = maxList.get(0);
		} else {
			Random r = new Random();
			node = maxList.get(r.nextInt(maxList.size()));
		}
		GoPoint resultPoint = new GoPoint(node.getX(), node.getY());
		resultPoint.setNodeNum(nodeNum);
		return resultPoint;
	}
	public static void computeGobanTree(GobanTree tree, GobanNode node, int level, int totalLevel) {
		tree.setNodeNum(tree.getNodeNum() + 1);
		GoState goType = node.getGoType();
		double nodeScore = node.getTotalProb() * node.getAttackScore() / ((totalLevel-level+1)/2+1);
		if(tree.isSameGoType(goType)) {
			tree.addSocre(nodeScore);
		} else {
			tree.addEnemyScore(nodeScore);
		}
		if(level<=1 || node.getAttackScore()>499.9) {
			
			return;
		}
		GoState enemyType;
		if(goType == GoState.BLACK) {
			enemyType = GoState.WHITE;
		} else {
			enemyType = GoState.BLACK;
		}
		List<GoNode> emptyList = LocalAIUtil.getAllValueableEmptyGoNode(node.getGoban(), 1);

		List<GobanNode> childList = new ArrayList<GobanNode>();
		for(GoNode goNode : emptyList) {
			GobanNode childNode = new GobanNode(node.getGoban().clone(), enemyType, goNode.getX(), goNode.getY());
			childList.add(childNode);
		}

		cutChild(childList);
		double totalScore = 0.0;
		for(GobanNode childNode : childList) {
			double score = childNode.getScore();
			totalScore += score;
		}
		//int num = 0;
		//int size = childList.size();
		//System.out.println("Level "+level+": "+size);
		for(GobanNode childNode : childList) {
			double prob = childNode.getScore() / totalScore;
			childNode.setLevelProb(prob);
			childNode.setTotalProb(node.getTotalProb() * prob);
			//num++;
			//System.out.println("Level "+level+": "+num+"/"+size);
			computeGobanTree(tree, childNode, level-1, totalLevel);
		}
	}
	public static void cutChild(List<GobanNode> childList) {
		double max = -Double.MAX_VALUE;
		for(GobanNode childNode : childList) {
			double score = childNode.getScore();
			if(score > max) {
				max = score;
			}
		}
		List<GobanNode> cutList = new ArrayList<GobanNode>();
		if(max > 100) {
			for(GobanNode childNode : childList) {
				double score = childNode.getScore();
				if(score < max) {
					cutList.add(childNode);
				}
			}
			childList.removeAll(cutList);
		}		
	}
}
